package com.android.dialer.extendboard;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import com.android.dialer.common.LogUtil;
import com.android.dialer.main.impl.MainActivity;
import com.google.gson.Gson;

import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;


public class ControllerService extends Service {
    private static final String TAG = "UsbService";

    private static final int NOTIFICATION_ID = 13333;
    private static final String CHANNEL_ID = "ForegroundServiceChannel";
    private ISmallControllerCallback mCallback;
    private IHookStateCallback mHookStateCallback;
    private UsbHelper usbHelper;
    //private UsbHelperTCP usbHelper;
    private ControllerHandler controllerHandler;
    private KeyEventHandler keyEventHandler;
    private int bindingApplicationPID = -1;

    private Map<String, String> callInfoMap;

    private List<String> activities;

    private CallStateReceiver callStateReceiver;

    private class CallStateReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (ServiceConstant.ACTION_CALL_STATE_CHANGED.equals(action)) {
                String state = intent.getStringExtra("state");
                String number = intent.getStringExtra("number");

                if (callInfoMap.containsKey(number)) {
                    if (callInfoMap.get(number).equals(state)) {
                        // LogUtil.i(TAG, "Call state is repeated");
                        return;
                    }else {
                        LogUtil.i(TAG, "Call state is changed, from " + callInfoMap.get(number) + " to " + state);
                    }
                }else if (!state.equals("INCOMING") && !state.equals("OUTGOING")) {
                    // LogUtil.i(TAG, "it should be redundant information");
                    return;
                }

                LogUtil.i(TAG, "Call state changed: " + state + ", number: " + number);

                callInfoMap.put(number, state);

                SpeedDialItem item = null;
                SpeedDialItem foundItem = null;
                ContentResolver contentResolver = getContentResolver();
                Cursor cursor = null;
                try {
                    cursor = contentResolver.query(PhonesContentProvider.CONTENT_URI, null, null, null, null);
                    if (cursor != null && cursor.moveToFirst()) {
                        do {
                            String value = cursor.getString(cursor.getColumnIndex(PhonesContentProvider.COLUMN_VALUE));
                            item = new Gson().fromJson(value, SpeedDialItem.class);
                            if (!item.getNumber().trim().isEmpty() && number.trim().contains(item.getNumber().trim())) {
                                LogUtil.i(TAG, "found item: " + item + " number is:" + number);
                                foundItem = item;
                                break;
                            }
                        } while (cursor.moveToNext());
                    }
                } finally {
                    if (cursor != null) {
                        cursor.close();
                    }
                }

                if (foundItem != null) {
                    USBMessage message = new USBMessage(USBMessage.MESSAGE_TYPE_CONTROL);
                    ByteBuffer buffer = ByteBuffer.allocate(1024).order(ByteOrder.LITTLE_ENDIAN);
                    buffer.putInt(foundItem.getId());

                    // send the item to usb
                    if (state.equals("OUTGOING")) {
                        buffer.putInt(2); // 1 mean led on, 0 mean off, 2 mean flash
                    }else if (state.equals("INCOMING")) {
                        buffer.putInt(2);
                    }else if (state.equals("DISCONNECT")) {
                        buffer.putInt(0);
                        callInfoMap.remove(number);
                    }else if (state.equals("INCALL")) {
                        buffer.putInt(1);
                    }

                    byte[] data = new byte[buffer.position()];
                    buffer.flip();
                    buffer.get(data);
                    message.setData(data);
                    usbHelper.sendData(message.toByteArray());
                }
            }
        }
    }


    private class PhonesContentObserver extends android.database.ContentObserver {
        public PhonesContentObserver(Handler handler) {
            super(handler);
        }

        @Override
        public void onChange(boolean selfChange, Uri uri) {
            super.onChange(selfChange);
            // 取出uri的最后一段作为key
            String key = uri.getLastPathSegment();
            LogUtil.i(TAG, "onChange: " + uri.toString() + ", key: " + key);
            if (key != null) {
                ContentResolver contentResolver = getContentResolver();
                Cursor cursor = contentResolver.query(PhonesContentProvider.CONTENT_URI, null, "", new String[]{key}, null);
                if (cursor != null) {
                    try {
                        if (cursor.moveToFirst()) {
                            String value = cursor.getString(cursor.getColumnIndex(PhonesContentProvider.COLUMN_VALUE));
                            SpeedDialItem item = new Gson().fromJson(value, SpeedDialItem.class);
                            LogUtil.i(TAG, "onChange: " + item.toString());
                            if (item != null) {
                                controllerHandler.sendMessage(controllerHandler.obtainMessage(ServiceConstant.MSG_TYPE_USB_SEND_CONFIG, item));
                            }
                        }
                    } finally {
                        cursor.close();
                    }
                }
            }

        }
    }

    private static class ControllerHandler extends Handler {
        private final WeakReference<ControllerService> serviceReference;

        public ControllerHandler(ControllerService service) {
            super(service.getMainLooper());
            serviceReference = new WeakReference<>(service);
        }

        @Override
        public void handleMessage(android.os.Message msg) {
            ControllerService service = serviceReference.get();
            if (service != null) {
                // Handle the message
                switch (msg.what) {
                    case ServiceConstant.MSG_TYPE_USB_SEND_CONFIG: {
                        USBMessage configMsg = new USBMessage(USBMessage.MESSAGE_TYPE_CONFIG);
                        ByteBuffer buffer = ByteBuffer.allocate(1024).order(ByteOrder.LITTLE_ENDIAN);

                        if (msg.obj == null) {
                            ContentResolver contentResolver = service.getContentResolver();
                            Cursor cursor = contentResolver.query(PhonesContentProvider.CONTENT_URI, null, null, null, null);
                            if (cursor != null) {

                                try {
                                    buffer.putInt(cursor.getCount());
                                    while (cursor.moveToNext()) {
                                        String key = cursor.getString(cursor.getColumnIndex(PhonesContentProvider.COLUMN_KEY));
                                        String value = cursor.getString(cursor.getColumnIndex(PhonesContentProvider.COLUMN_VALUE));
                                        service.usbLog("Key: " + key + ",\nValue: " + value);
                                        SpeedDialItem item = new Gson().fromJson(value, SpeedDialItem.class);
                                        if (item != null) {
                                            byte[] itemData = item.toByteArray();
                                            buffer.putInt(itemData.length);
                                            buffer.put(itemData);
                                        }
                                    }
                                    if (cursor.isClosed()) {
                                        service.usbLog("cursor is closed");
                                    }
                                } finally {
                                    cursor.close();
                                }

                                byte[] data = new byte[buffer.position()];
                                buffer.flip();
                                buffer.get(data);
                                configMsg.setData(data);
                                service.usbHelper.sendData(configMsg.toByteArray());
                            } else {
                                service.usbLog("Cursor is null");
                            }
                        } else {
                            SpeedDialItem item = (SpeedDialItem) msg.obj;
                            byte[] itemData = item.toByteArray();
                            buffer.putInt(1);
                            buffer.putInt(itemData.length);
                            buffer.put(itemData);

                            byte[] data = new byte[buffer.position()];
                            buffer.flip();
                            buffer.get(data);
                            configMsg.setData(data);
                            service.usbHelper.sendData(configMsg.toByteArray());
                        }
                    }
                    break;

                    case ServiceConstant.MSG_TYPE_USB_RE_CONNECT: {
                        // Handle message type 2
                        if (!service.usbHelper.connectUsbDevice()) {
                            service.controllerHandler.sendEmptyMessageDelayed(ServiceConstant.MSG_TYPE_USB_RE_CONNECT, 1000);
                        }
                    }break;

                    case ServiceConstant.MSG_TYPE_USB_LOG_STRING: {
                        // Handle message type 3
                        service.usbLog((String)msg.obj);
                    }break;

                    case ServiceConstant.MSG_TYPE_USB_CONNECT: {
                        // Handle message type 4
                        service.usbLog("USB connected");
                        service.controllerHandler.sendEmptyMessageDelayed(ServiceConstant.MSG_TYPE_USB_SEND_CONFIG, 100);

                       // service.controllerHandler.sendMessageDelayed(service.controllerHandler.obtainMessage(ServiceConstant.MSG_TYPE_TEST_LED, id++, 2), 1000);
                    }
                    break;

                    case ServiceConstant.MSG_TYPE_USB_PERMISSION_DENIED: {
                        // Handle message type 4
                        if (service.usbHelper.checkUsbPermissionBeforeConnect()) {
                            if (!service.usbHelper.connectUsbDevice()) {
                                service.controllerHandler.sendEmptyMessageDelayed(ServiceConstant.MSG_TYPE_USB_RE_CONNECT, 1000);
                            }
                        }
                    }
                    break;

                    case ServiceConstant.MSG_TYPE_USB_GET_MESSAGE: {
                        USBMessage message = (USBMessage) msg.obj;
                        if (message != null) {
                            service.usbLog("USB message received: " + message.toString());
                            // Handle the USB message
                            if (message.getMessageType() == USBMessage.MESSAGE_TYPE_KEY_EVENT) {
                                // Handle key event
                                service.keyEventHandler.handleKeyEvent(message);
                            } else if (message.getMessageType() == USBMessage.MESSAGE_TYPE_HOOK_STATE) {
                                ByteBuffer buffer = ByteBuffer.wrap(message.getData()).order(ByteOrder.LITTLE_ENDIAN);
                                int hookState = buffer.getInt();
                                try {
                                    if (service.mHookStateCallback != null) {
                                        service.mHookStateCallback.onHookState(hookState);
                                        service.mHookStateCallback = null;
                                    }
                                } catch (RemoteException e) {
                                    LogUtil.e(TAG, "Error calling mHookStateCallback", e);
                                }
                            }else {
                                service.usbLog("Unknown message type: " + message.getMessageType());
                            }
                        }
                    }
                    break;

                    case ServiceConstant.MSG_TYPE_USB_GET_HOOK_STATE: {
                        LogUtil.i(TAG, "MSG_TYPE_USB_GET_HOOK_STATE");
                        USBMessage message = new USBMessage(USBMessage.MESSAGE_TYPE_GET_HOOK_STATE);
                        ByteBuffer buffer = ByteBuffer.allocate(1024).order(ByteOrder.LITTLE_ENDIAN);
                        service.usbHelper.sendData(message.toByteArray());
                    }
                    break;

                    case ServiceConstant.MSG_TYPE_SWITCH_MIC: {
                        int state = (int) msg.arg1;
                        LogUtil.i(TAG, "MSG_TYPE_SWITCH_MIC: " + state);
                        service.switchMic(state);
                    }
                    break;

                    default:
                        break;
                }
            }
        }
    }

    private final ISmallControllerService.Stub binder = new ISmallControllerService.Stub() {
        @Override
        public void sendMessage(Message message) {
            LogUtil.i(TAG, "sendMessage: type=" + message.what + ", data=" + message.toString());
            Message msg = controllerHandler.obtainMessage();
            msg.what = message.what;
            msg.obj = message.obj;
            controllerHandler.sendMessage(msg);
        }

        @Override
        public void registerMessgeCallback(ISmallControllerCallback callback, String key) {
            LogUtil.i(TAG, "registerMessenger key:" + key);
            keyEventHandler.registerMessenger(callback, key);
        }

        @Override
        public void unregisterMessageCallback(String key) {
            LogUtil.i(TAG, "unregisterMessenger key:", key);
            keyEventHandler.unregisterMessenger(key);
        }

        @Override
        public void getHookState(IHookStateCallback callback) {
            LogUtil.i(TAG, "getHookState usbHelper is null ?: " + (usbHelper == null));
            if (usbHelper != null) {
                mHookStateCallback = callback;
                USBMessage message = new USBMessage(USBMessage.MESSAGE_TYPE_GET_HOOK_STATE);
                usbHelper.sendData(message.toByteArray());
            }
        }
    };

    public void switchMic(int state) {
        USBMessage message = new USBMessage(USBMessage.MESSAGE_TYPE_SWITCH_MIC_STATE);
        ByteBuffer buffer = ByteBuffer.allocate(1024).order(ByteOrder.LITTLE_ENDIAN);
        buffer.putInt(state);
        byte[] data = new byte[buffer.position()];
        buffer.flip();
        buffer.get(data);
        message.setData(data);
        usbHelper.sendData(message.toByteArray());
    }


    @Override
    public IBinder onBind(Intent intent) {
        LogUtil.i(TAG, "onBind...............");
        return binder;
    }

    // unbind function
    @Override
    public boolean onUnbind(Intent intent) {
        LogUtil.i(TAG, "onUnbind---------------->");
        return super.onUnbind(intent);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        LogUtil.i(TAG, "onStartCommand.............." + startId + ", flags: " + flags + ", intent: " + intent);

        return START_STICKY;
    }

    private Notification createForegroundNotification() {
        NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

        // 唯一的通知通道的id.
        String notificationChannelId = "notification_channel_id_01";

        // 用户可见的通道名称
        String channelName = "Foreground Service Notification";
        // 通道的重要程度
        int importance = NotificationManager.IMPORTANCE_HIGH;
        NotificationChannel notificationChannel = new NotificationChannel(notificationChannelId, channelName, importance);
        notificationChannel.setDescription("Channel description");
        if (notificationManager != null) {
            notificationManager.createNotificationChannel(notificationChannel);
        }

        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, notificationChannelId);
        // 通知小图标
        builder.setSmallIcon(R.mipmap.ic_launcher);
        // 通知标题
        builder.setContentTitle("Dialer");
        // 通知内容
        builder.setContentText("Dialer is running...");
        // 设定通知显示的时间
        builder.setWhen(System.currentTimeMillis());
        // 设定启动的内容
        Intent activityIntent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(
                this,
                1,
                activityIntent,
                PendingIntent.FLAG_IMMUTABLE
        ); // FLAG_UPDATE_CURRENT
        builder.setContentIntent(pendingIntent);

        // 创建通知并返回
        return builder.build();
    }


    @Override
    public void onCreate() {
        super.onCreate();
        LogUtil.i(TAG, "onCreate...........................");

        // check whether controllerHandler is null, and keyEventHandler is null
        controllerHandler = new ControllerHandler(this);
        keyEventHandler = new KeyEventHandler(this);
        usbHelper = new UsbHelper(getApplicationContext(), controllerHandler);
        //usbHelper = new UsbHelperTCP(getApplicationContext(), controllerHandler);

        if (usbHelper.checkUsbConnections()) {
            if (usbHelper.checkUsbPermissionBeforeConnect()) {
                if (!usbHelper.connectUsbDevice()) {
                    controllerHandler.sendEmptyMessageDelayed(ServiceConstant.MSG_TYPE_USB_RE_CONNECT, 1000);
                }
            }
        }

        LogUtil.i(TAG, "onCreate: content_uri is " + PhonesContentProvider.CONTENT_URI.toString());
        getContentResolver().registerContentObserver(PhonesContentProvider.CONTENT_URI, true, new PhonesContentObserver(controllerHandler));

        LogUtil.i(TAG, "onCreate: register content observer:" + ServiceConstant.ACTION_CALL_STATE_CHANGED);
        IntentFilter filter = new IntentFilter();
        filter.addAction(ServiceConstant.ACTION_CALL_STATE_CHANGED);
        callStateReceiver = new CallStateReceiver();
        registerReceiver(callStateReceiver, filter, "com.android.dialer.permission.CALL_STATE_CHANGED", null);

        callInfoMap = new HashMap<>();

        Notification notification = createForegroundNotification();
        startForeground(NOTIFICATION_ID, notification);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        LogUtil.i(TAG, "onDestroy------------------------->");
        if (usbHelper != null) {
            usbHelper.destroy();
        }
        mCallback = null;
        unregisterReceiver(callStateReceiver);
        callStateReceiver = null;
        stopForeground(Service.STOP_FOREGROUND_REMOVE);
    }

    public void usbLog(String log) {
        LogUtil.i(TAG, log);
        ServiceMessage message = new ServiceMessage(ServiceConstant.MSG_TYPE_USB_LOG_STRING, log.getBytes());
    }

}
